home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet internetowy / Przegladarki internetowe / Mozilla Seamonkey 1.0.5 pl / seamonkey-1.0.5.pl-PL.win32.installer.exe / BROWSER.XPI / bin / chrome / toolkit.jar / content / global / filepicker.js < prev    next >
Encoding:
Text File  |  2005-03-11  |  25.5 KB  |  852 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * ***** BEGIN LICENSE BLOCK *****
  4.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5.  *
  6.  * The contents of this file are subject to the Mozilla Public License Version
  7.  * 1.1 (the "License"); you may not use this file except in compliance with
  8.  * the License. You may obtain a copy of the License at
  9.  * http://www.mozilla.org/MPL/
  10.  *
  11.  * Software distributed under the License is distributed on an "AS IS" basis,
  12.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13.  * for the specific language governing rights and limitations under the
  14.  * License.
  15.  *
  16.  * The Original Code is mozilla.org code.
  17.  *
  18.  * The Initial Developer of the Original Code is
  19.  * Netscape Communications Corporation.
  20.  * Portions created by the Initial Developer are Copyright (C) 2000
  21.  * the Initial Developer. All Rights Reserved.
  22.  *
  23.  * Contributor(s):
  24.  *   Stuart Parmenter <pavlov@netscape.com>
  25.  *   Brian Ryner <bryner@brianryner.com>
  26.  *   Jan Varga <varga@ku.sk>
  27.  *   Peter Annema <disttsc@bart.nl>
  28.  *   Johann Petrak <johann@ai.univie.ac.at>
  29.  *   Akkana Peck <akkana@netscape.com>
  30.  *
  31.  * Alternatively, the contents of this file may be used under the terms of
  32.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  33.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  34.  * in which case the provisions of the GPL or the LGPL are applicable instead
  35.  * of those above. If you wish to allow use of your version of this file only
  36.  * under the terms of either the GPL or the LGPL, and not to allow others to
  37.  * use your version of this file under the terms of the MPL, indicate your
  38.  * decision by deleting the provisions above and replace them with the notice
  39.  * and other provisions required by the GPL or the LGPL. If you do not delete
  40.  * the provisions above, a recipient may use your version of this file under
  41.  * the terms of any one of the MPL, the GPL or the LGPL.
  42.  *
  43.  * ***** END LICENSE BLOCK ***** */
  44.  
  45. const nsIFilePicker       = Components.interfaces.nsIFilePicker;
  46. const nsIProperties       = Components.interfaces.nsIProperties;
  47. const NS_DIRECTORYSERVICE_CONTRACTID = "@mozilla.org/file/directory_service;1";
  48. const nsITreeBoxObject = Components.interfaces.nsITreeBoxObject;
  49. const nsIFileView = Components.interfaces.nsIFileView;
  50. const NS_FILEVIEW_CONTRACTID = "@mozilla.org/filepicker/fileview;1";
  51. const nsITreeView = Components.interfaces.nsITreeView;
  52. const nsILocalFile = Components.interfaces.nsILocalFile;
  53. const nsIFile = Components.interfaces.nsIFile;
  54. const NS_LOCALFILE_CONTRACTID = "@mozilla.org/file/local;1";
  55. const NS_PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1";
  56.  
  57. var sfile = Components.classes[NS_LOCALFILE_CONTRACTID].createInstance(nsILocalFile);
  58. var retvals;
  59. var filePickerMode;
  60. var homeDir;
  61. var treeView;
  62.  
  63. var textInput;
  64. var okButton;
  65.  
  66. var gFilePickerBundle;
  67.  
  68. // name of new directory entered by the user to be remembered
  69. // for next call of newDir() in case something goes wrong with creation
  70. var gNewDirName = { value: "" };
  71.  
  72. function filepickerLoad() {
  73.   gFilePickerBundle = document.getElementById("bundle_filepicker");
  74.  
  75.   textInput = document.getElementById("textInput");
  76.   okButton = document.documentElement.getButton("accept");
  77.   treeView = Components.classes[NS_FILEVIEW_CONTRACTID].createInstance(nsIFileView);
  78.  
  79.   if (window.arguments) {
  80.     var o = window.arguments[0];
  81.     retvals = o.retvals; /* set this to a global var so we can set return values */
  82.     const title = o.title;
  83.     filePickerMode = o.mode;
  84.     if (o.displayDirectory) {
  85.       const directory = o.displayDirectory.path;
  86.     }
  87.     const initialText = o.defaultString;
  88.     const filterTitles = o.filters.titles;
  89.     const filterTypes = o.filters.types;
  90.     const numFilters = filterTitles.length;
  91.  
  92.     document.title = title;
  93.  
  94.     if (initialText) {
  95.       textInput.value = initialText;
  96.     }
  97.   }
  98.  
  99.   if (filePickerMode != nsIFilePicker.modeOpen && filePickerMode != nsIFilePicker.modeOpenMultiple) {
  100.     var newDirButton = document.getElementById("newDirButton");
  101.     newDirButton.removeAttribute("hidden");
  102.   }
  103.  
  104.   if (filePickerMode == nsIFilePicker.modeGetFolder) {
  105.     var textInputLabel = document.getElementById("textInputLabel");
  106.     textInputLabel.value = gFilePickerBundle.getString("dirTextInputLabel");
  107.   }
  108.   
  109.   if ((filePickerMode == nsIFilePicker.modeOpen) ||
  110.       (filePickerMode == nsIFilePicker.modeOpenMultiple) ||
  111.       (filePickerMode == nsIFilePicker.modeSave)) {
  112.  
  113.     /* build filter popup */
  114.     var filterPopup = document.createElement("menupopup");
  115.  
  116.     for (var i = 0; i < numFilters; i++) {
  117.       var menuItem = document.createElement("menuitem");
  118.       if (filterTypes[i] == "..apps")
  119.         menuItem.setAttribute("label", filterTitles[i]);
  120.       else
  121.         menuItem.setAttribute("label", filterTitles[i] + " (" + filterTypes[i] + ")");
  122.       menuItem.setAttribute("filters", filterTypes[i]);
  123.       filterPopup.appendChild(menuItem);
  124.     }
  125.  
  126.     var filterMenuList = document.getElementById("filterMenuList");
  127.     filterMenuList.appendChild(filterPopup);
  128.     if (numFilters > 0)
  129.       filterMenuList.selectedIndex = 0;
  130.     var filterBox = document.getElementById("filterBox");
  131.     filterBox.removeAttribute("hidden");
  132.  
  133.     filterMenuList.selectedIndex = o.filterIndex;
  134.  
  135.     treeView.setFilter(filterTypes[o.filterIndex]);
  136.  
  137.   } else if (filePickerMode == nsIFilePicker.modeGetFolder) {
  138.     treeView.showOnlyDirectories = true;
  139.   }
  140.  
  141.   // start out with a filename sort
  142.   handleColumnClick("FilenameColumn");
  143.  
  144.   try {
  145.     var buttonLabel = getOKAction();
  146.     okButton.setAttribute("label", buttonLabel);
  147.   } catch (exception) {
  148.     // keep it set to "OK"
  149.   }
  150.  
  151.   // setup the dialogOverlay.xul button handlers
  152.   retvals.buttonStatus = nsIFilePicker.returnCancel;
  153.  
  154.   var tree = document.getElementById("directoryTree");
  155.   if (filePickerMode == nsIFilePicker.modeOpenMultiple)
  156.     tree.removeAttribute("seltype");
  157.  
  158.   tree.treeBoxObject.view = treeView;
  159.  
  160.   // Start out with the ok button disabled since nothing will be
  161.   // selected and nothing will be in the text field.
  162.   okButton.disabled = filePickerMode != nsIFilePicker.modeGetFolder;
  163.  
  164.   // This allows the window to show onscreen before we begin
  165.   // loading the file list
  166.  
  167.   setTimeout(setInitialDirectory, 0, directory);
  168. }
  169.  
  170. function setInitialDirectory(directory)
  171. {
  172.   // Start in the user's home directory
  173.   var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID]
  174.                              .getService(nsIProperties);
  175.   homeDir = dirService.get("Home", Components.interfaces.nsIFile);
  176.  
  177.   if (directory) {
  178.     sfile.initWithPath(directory);
  179.     if (!sfile.exists() || !sfile.isDirectory())
  180.       directory = false;
  181.   }
  182.   if (!directory) {
  183.     sfile.initWithPath(homeDir.path);
  184.   }
  185.  
  186.   gotoDirectory(sfile);
  187. }
  188.  
  189. function onFilterChanged(target)
  190. {
  191.   // Do this on a timeout callback so the filter list can roll up
  192.   // and we don't keep the mouse grabbed while we are refiltering.
  193.  
  194.   setTimeout(changeFilter, 0, target.getAttribute("filters"));
  195. }
  196.  
  197. function changeFilter(filterTypes)
  198. {
  199.   window.setCursor("wait");
  200.   treeView.setFilter(filterTypes);
  201.   window.setCursor("auto");
  202. }
  203.  
  204. function showErrorDialog(titleStrName, messageStrName, file)
  205. {
  206.   var errorTitle =
  207.     gFilePickerBundle.getFormattedString(titleStrName, [file.path]);
  208.   var errorMessage =
  209.     gFilePickerBundle.getFormattedString(messageStrName, [file.path]);
  210.   var promptService =
  211.     Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  212.  
  213.   promptService.alert(window, errorTitle, errorMessage);
  214. }
  215.  
  216. function openOnOK()
  217. {
  218.   var dir = treeView.selectedFiles.queryElementAt(0, nsIFile);
  219.   if (dir)
  220.     gotoDirectory(dir);
  221.  
  222.   return false;
  223. }
  224.  
  225. function selectOnOK()
  226. {
  227.   var errorTitle, errorMessage, promptService;
  228.   var ret = nsIFilePicker.returnOK;
  229.  
  230.   var isDir = false;
  231.   var isFile = false;
  232.  
  233.   var fileList = processPath(textInput.value);
  234.  
  235.   if (!fileList) { // generic error message, should probably never happen
  236.     showErrorDialog("errorPathProblemTitle",
  237.                     "errorPathProblemMessage",
  238.                     textInput.value);
  239.     return false;
  240.   }
  241.  
  242.   var curFileIndex;
  243.   for (curFileIndex = 0; curFileIndex < fileList.length &&
  244.          ret != nsIFilePicker.returnCancel; ++curFileIndex) {
  245.     var file = fileList[curFileIndex].QueryInterface(nsIFile);
  246.  
  247.     // try to normalize - if this fails we will ignore the error
  248.     // because we will notice the
  249.     // error later and show a fitting error alert.
  250.     try{
  251.       file.normalize();
  252.     } catch(e) {
  253.       //promptService.alert(window, "Problem", "normalize failed, continuing");
  254.     }
  255.  
  256.     var fileExists = file.exists();
  257.  
  258.     if (!fileExists && (filePickerMode == nsIFilePicker.modeOpen ||
  259.                         filePickerMode == nsIFilePicker.modeOpenMultiple)) {
  260.       showErrorDialog("errorOpenFileDoesntExistTitle",
  261.                       "errorOpenFileDoesntExistMessage",
  262.                       file);
  263.       return false;
  264.     }
  265.  
  266.     if (!fileExists && filePickerMode == nsIFilePicker.modeGetFolder) {
  267.       showErrorDialog("errorDirDoesntExistTitle",
  268.                       "errorDirDoesntExistMessage",
  269.                       file);
  270.       return false;
  271.     }
  272.  
  273.     if (fileExists) {
  274.       isDir = file.isDirectory();
  275.       isFile = file.isFile();
  276.     }
  277.  
  278.     switch(filePickerMode) {
  279.     case nsIFilePicker.modeOpen:
  280.     case nsIFilePicker.modeOpenMultiple:
  281.       if (isFile) {
  282.         if (file.isReadable()) {
  283.           retvals.directory = file.parent.path;
  284.         } else {
  285.           showErrorDialog("errorOpeningFileTitle",
  286.                           "openWithoutPermissionMessage_file",
  287.                           file);
  288.           ret = nsIFilePicker.returnCancel;
  289.         }
  290.       } else if (isDir) {
  291.         if (!sfile.equals(file)) {
  292.           gotoDirectory(file);
  293.         }
  294.         textInput.value = "";
  295.         doEnabling();
  296.         ret = nsIFilePicker.returnCancel;
  297.       }
  298.       break;
  299.     case nsIFilePicker.modeSave:
  300.       if (isFile) { // can only be true if file.exists()
  301.         if (!file.isWritable()) {
  302.           showErrorDialog("errorSavingFileTitle",
  303.                           "saveWithoutPermissionMessage_file",
  304.                           file);
  305.           ret = nsIFilePicker.returnCancel;
  306.         } else {
  307.           // we need to pop up a dialog asking if you want to save
  308.           var confirmTitle = gFilePickerBundle.getString("confirmTitle");
  309.           var message =
  310.             gFilePickerBundle.getFormattedString("confirmFileReplacing",
  311.                                                  [file.path]);
  312.           
  313.           promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  314.           var rv = promptService.confirm(window, title, message);
  315.           if (rv) {
  316.             ret = nsIFilePicker.returnReplace;
  317.             retvals.directory = file.parent.path;
  318.           } else {
  319.             ret = nsIFilePicker.returnCancel;
  320.           }
  321.         }
  322.       } else if (isDir) {
  323.         if (!sfile.equals(file)) {
  324.           gotoDirectory(file);
  325.         }
  326.         textInput.value = "";
  327.         doEnabling();
  328.         ret = nsIFilePicker.returnCancel;
  329.       } else {
  330.         var parent = file.parent;
  331.         if (parent.exists() && parent.isDirectory() && parent.isWritable()) {
  332.           retvals.directory = parent.path;
  333.         } else {
  334.           var oldParent = parent;
  335.           while (!parent.exists()) {
  336.             oldParent = parent;
  337.             parent = parent.parent;
  338.           }
  339.           errorTitle =
  340.             gFilePickerBundle.getFormattedString("errorSavingFileTitle",
  341.                                                  [file.path]);
  342.           if (parent.isFile()) {
  343.             errorMessage =
  344.               gFilePickerBundle.getFormattedString("saveParentIsFileMessage",
  345.                                                    [parent.path, file.path]);
  346.           } else {
  347.             errorMessage =
  348.               gFilePickerBundle.getFormattedString("saveParentDoesntExistMessage",
  349.                                                    [oldParent.path, file.path]);
  350.           }
  351.           if (!parent.isWritable()) {
  352.             errorMessage =
  353.               gFilePickerBundle.getFormattedString("saveWithoutPermissionMessage_dir", [parent.path]);
  354.           }
  355.           promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  356.           promptService.alert(window, errorTitle, errorMessage);
  357.           ret = nsIFilePicker.returnCancel;
  358.         }
  359.       }
  360.       break;
  361.     case nsIFilePicker.modeGetFolder:
  362.       if (isDir) {
  363.         retvals.directory = file.parent.path;
  364.       } else { // if nothing selected, the current directory will be fine
  365.         retvals.directory = sfile.path;
  366.       }
  367.       break;
  368.     }
  369.   }
  370.  
  371.   gFilesEnumerator.mFiles = fileList;
  372.  
  373.   retvals.files = gFilesEnumerator;
  374.   retvals.buttonStatus = ret;
  375.  
  376.   var filterMenuList = document.getElementById("filterMenuList");
  377.   retvals.filterIndex = filterMenuList.selectedIndex;
  378.   
  379.   return (ret != nsIFilePicker.returnCancel);
  380. }
  381.  
  382. var gFilesEnumerator = {
  383.   mFiles: null,
  384.   mIndex: 0,
  385.  
  386.   hasMoreElements: function()
  387.   {
  388.     return (this.mIndex < this.mFiles.length);
  389.   },
  390.   getNext: function()
  391.   {
  392.     if (this.mIndex >= this.mFiles.length)
  393.       throw Components.results.NS_ERROR_FAILURE;
  394.     return this.mFiles[this.mIndex++];
  395.   }
  396. };
  397.  
  398. function onCancel()
  399. {
  400.   // Close the window.
  401.   retvals.buttonStatus = nsIFilePicker.returnCancel;
  402.   retvals.file = null;
  403.   retvals.files = null;
  404.   return true;
  405. }
  406.  
  407. function onDblClick(e) {
  408.   // we only care about button 0 (left click) events
  409.   if (e.button != 0) return;
  410.  
  411.   var t = e.originalTarget;
  412.   if (t.localName != "treechildren")
  413.     return;
  414.  
  415.   openSelectedFile();
  416. }
  417.  
  418. function openSelectedFile() {
  419.   var fileList = treeView.selectedFiles;
  420.   if (fileList.length == 0)
  421.     return;
  422.  
  423.   var file = fileList.queryElementAt(0, nsIFile);
  424.   if (file.isDirectory())
  425.     gotoDirectory(file);
  426.   else if (file.isFile())
  427.     document.documentElement.acceptDialog();
  428. }
  429.  
  430. function onClick(e) {
  431.   var t = e.originalTarget;
  432.   if (t.localName == "treecol")
  433.     handleColumnClick(t.id);
  434. }
  435.  
  436. function convertColumnIDtoSortType(columnID) {
  437.   var sortKey;
  438.   
  439.   switch (columnID) {
  440.   case "FilenameColumn":
  441.     sortKey = nsIFileView.sortName;
  442.     break;
  443.   case "FileSizeColumn":
  444.     sortKey = nsIFileView.sortSize;
  445.     break;
  446.   case "LastModifiedColumn":
  447.     sortKey = nsIFileView.sortDate;
  448.     break;
  449.   default:
  450.     dump("unsupported sort column: " + columnID + "\n");
  451.     sortKey = 0;
  452.     break;
  453.   }
  454.   
  455.   return sortKey;
  456. }
  457.  
  458. function handleColumnClick(columnID) {
  459.   var sortType = convertColumnIDtoSortType(columnID);
  460.   var sortOrder = (treeView.sortType == sortType) ? !treeView.reverseSort : false;
  461.   treeView.sort(sortType, sortOrder);
  462.   
  463.   // set the sort indicator on the column we are sorted by
  464.   var sortedColumn = document.getElementById(columnID);
  465.   if (treeView.reverseSort) {
  466.     sortedColumn.setAttribute("sortDirection", "descending");
  467.   } else {
  468.     sortedColumn.setAttribute("sortDirection", "ascending");
  469.   }
  470.   
  471.   // remove the sort indicator from the rest of the columns
  472.   var currCol = sortedColumn.parentNode.firstChild;
  473.   while (currCol) {
  474.     if (currCol != sortedColumn && currCol.localName == "treecol")
  475.       currCol.removeAttribute("sortDirection");
  476.     currCol = currCol.nextSibling;
  477.   }
  478. }
  479.  
  480. function onKeypress(e) {
  481.   if (e.keyCode == 8) /* backspace */
  482.     goUp();
  483.  
  484.   /* enter is handled by the ondialogaccept handler */
  485. }
  486.  
  487. function doEnabling() {
  488.   if (filePickerMode != nsIFilePicker.modeGetFolder)
  489.   // Maybe add check if textInput.value would resolve to an existing
  490.   // file or directory in .modeOpen. Too costly I think.
  491.     okButton.disabled = (textInput.value == "")
  492. }
  493.  
  494. function onTreeFocus(event) {
  495.   // Reset the button label and enabled/disabled state.
  496.   onFileSelected(treeView.selectedFiles);
  497. }
  498.  
  499. function getOKAction(file) {
  500.   var buttonLabel;
  501.  
  502.   if (file && file.isDirectory()) {
  503.     document.documentElement.setAttribute("ondialogaccept", "return openOnOK();");
  504.     buttonLabel = gFilePickerBundle.getString("openButtonLabel");
  505.   }
  506.   else {
  507.     document.documentElement.setAttribute("ondialogaccept", "return selectOnOK();");
  508.     switch(filePickerMode) {
  509.     case nsIFilePicker.modeGetFolder:
  510.       buttonLabel = gFilePickerBundle.getString("selectFolderButtonLabel");
  511.       break;
  512.     case nsIFilePicker.modeOpen:
  513.     case nsIFilePicker.modeOpenMultiple:
  514.       buttonLabel = gFilePickerBundle.getString("openButtonLabel");
  515.       break;
  516.     case nsIFilePicker.modeSave:
  517.       buttonLabel = gFilePickerBundle.getString("saveButtonLabel");
  518.       break;
  519.     }
  520.   }
  521.  
  522.   return buttonLabel;
  523. }
  524.  
  525. function onSelect(event) {
  526.   onFileSelected(treeView.selectedFiles);
  527. }
  528.  
  529. function onFileSelected(/* nsIArray */ selectedFileList) {
  530.   var validFileSelected = false;
  531.   var invalidSelection = false;
  532.   var file;
  533.   var fileCount = selectedFileList.length;
  534.  
  535.   for (var index = 0; index < fileCount; ++index) {
  536.     file = selectedFileList.queryElementAt(index, nsIFile);
  537.     if (file) {
  538.       var path = file.leafName;
  539.  
  540.       if (path) {
  541.         var isDir = file.isDirectory();
  542.         if ((filePickerMode == nsIFilePicker.modeGetFolder) || !isDir) {
  543.           if (!validFileSelected)
  544.             textInput.value = "";
  545.           addToTextFieldValue(path);
  546.         }
  547.  
  548.         if (isDir && fileCount > 1) {
  549.           // The user has selected multiple items, and one of them is
  550.           // a directory.  This is not a valid state, so we'll disable
  551.           // the ok button.
  552.           invalidSelection = true;
  553.         }
  554.  
  555.         validFileSelected = true;
  556.       }
  557.     }
  558.   }
  559.  
  560.   if (validFileSelected) {
  561.     var buttonLabel = getOKAction(file);
  562.     okButton.setAttribute("label", buttonLabel);
  563.     okButton.disabled = invalidSelection;
  564.   } else if (filePickerMode != nsIFilePicker.modeGetFolder)
  565.     okButton.disabled = (textInput.value == "");
  566. }
  567.  
  568. function addToTextFieldValue(path)
  569. {
  570.   var newValue = "";
  571.  
  572.   if (textInput.value == "")
  573.     newValue = path.replace(/\"/g, "\\\"");
  574.   else {
  575.     // Quote the existing text if needed,
  576.     // then append the new filename (quoted and escaped)
  577.     if (textInput.value[0] != '"')
  578.       newValue = '"' + textInput.value.replace(/\"/g, "\\\"") + '"';
  579.     else
  580.       newValue = textInput.value;
  581.  
  582.     newValue = newValue + ' "' + path.replace(/\"/g, "\\\"") + '"';
  583.   }
  584.  
  585.   textInput.value = newValue;
  586. }
  587.  
  588. function onTextFieldFocus() {
  589.   var buttonLabel = getOKAction(null);
  590.   okButton.setAttribute("label", buttonLabel);
  591.   doEnabling();
  592. }
  593.  
  594. function onDirectoryChanged(target)
  595. {
  596.   var path = target.getAttribute("label");
  597.  
  598.   var file = Components.classes[NS_LOCALFILE_CONTRACTID].createInstance(nsILocalFile);
  599.   file.initWithPath(path);
  600.  
  601.   if (!sfile.equals(file)) {
  602.     // Do this on a timeout callback so the directory list can roll up
  603.     // and we don't keep the mouse grabbed while we are loading.
  604.  
  605.     setTimeout(gotoDirectory, 0, file);
  606.   }
  607. }
  608.  
  609. function populateAncestorList(directory) {
  610.   var menu = document.getElementById("lookInMenu");
  611.  
  612.   while (menu.hasChildNodes()) {
  613.     menu.removeChild(menu.firstChild);
  614.   }
  615.   
  616.   var menuItem = document.createElement("menuitem");
  617.   menuItem.setAttribute("label", directory.path);
  618.   menuItem.setAttribute("crop", "start");
  619.   menu.appendChild(menuItem);
  620.  
  621.   // .parent is _sometimes_ null, see bug 121489.  Do a dance around that.
  622.   var parent = directory.parent;
  623.   while (parent && !parent.equals(directory)) {
  624.     menuItem = document.createElement("menuitem");
  625.     menuItem.setAttribute("label", parent.path);
  626.     menuItem.setAttribute("crop", "start");
  627.     menu.appendChild(menuItem);
  628.     directory = parent;
  629.     parent = directory.parent;
  630.   }
  631.   
  632.   var menuList = document.getElementById("lookInMenuList");
  633.   menuList.selectedIndex = 0;
  634. }
  635.  
  636. function goUp() {
  637.   try {
  638.     var parent = sfile.parent;
  639.   } catch(ex) { dump("can't get parent directory\n"); }
  640.  
  641.   if (parent) {
  642.     gotoDirectory(parent);
  643.   }
  644. }
  645.  
  646. function goHome() {
  647.   gotoDirectory(homeDir);
  648. }
  649.  
  650. function newDir() {
  651.   var file;
  652.   var promptService =
  653.     Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  654.   var dialogTitle =
  655.     gFilePickerBundle.getString("promptNewDirTitle");
  656.   var dialogMsg =
  657.     gFilePickerBundle.getString("promptNewDirMessage");
  658.   var ret = promptService.prompt(window, dialogTitle, dialogMsg, gNewDirName, null, {value:0});
  659.  
  660.   if (ret) {
  661.     file = processPath(gNewDirName.value);
  662.     if (!file) {
  663.       showErrorDialog("errorCreateNewDirTitle",
  664.                       "errorCreateNewDirMessage",
  665.                       file);
  666.       return false;
  667.     }
  668.     
  669.     file = file[0].QueryInterface(nsIFile);
  670.     if (file.exists()) {
  671.       showErrorDialog("errorNewDirDoesExistTitle",
  672.                       "errorNewDirDoesExistMessage",
  673.                       file);
  674.       return false;
  675.     }
  676.  
  677.     var parent = file.parent;
  678.     if (!(parent.exists() && parent.isDirectory() && parent.isWritable())) {
  679.       var oldParent = parent;
  680.       while (!parent.exists()) {
  681.         oldParent = parent;
  682.         parent = parent.parent;
  683.       }
  684.       if (parent.isFile()) {
  685.         showErrorDialog("errorCreateNewDirTitle",
  686.                         "errorCreateNewDirIsFileMessage",
  687.                         parent);
  688.         return false;
  689.       }
  690.       if (!parent.isWritable()) {
  691.         showErrorDialog("errorCreateNewDirTitle",
  692.                         "errorCreateNewDirPermissionMessage",
  693.                         parent);
  694.         return false;
  695.       }
  696.     }
  697.  
  698.     try {
  699.       file.create(nsIFile.DIRECTORY_TYPE, 0755); 
  700.     } catch (e) {
  701.       showErrorDialog("errorCreateNewDirTitle",
  702.                       "errorCreateNewDirMessage",
  703.                       file);
  704.       return false;
  705.     }
  706.     file.normalize(); // ... in case ".." was used in the path
  707.     gotoDirectory(file);
  708.     // we remember and reshow a dirname if something goes wrong
  709.     // so that errors can be corrected more easily. If all went well,
  710.     // reset the default value to blank
  711.     gNewDirName = { value: "" }; 
  712.   }
  713.   return true;
  714. }
  715.  
  716. function gotoDirectory(directory) {
  717.   window.setCursor("wait");
  718.   try {
  719.     populateAncestorList(directory);
  720.     treeView.setDirectory(directory);
  721.     document.getElementById("errorShower").selectedIndex = 0;
  722.   } catch(ex) {
  723.     document.getElementById("errorShower").selectedIndex = 1;
  724.   }
  725.  
  726.   window.setCursor("auto");
  727.  
  728.   treeView.QueryInterface(nsITreeView).selection.clearSelection();
  729.   if (filePickerMode == nsIFilePicker.modeGetFolder) {
  730.     textInput.value = "";
  731.   }
  732.   textInput.focus();
  733.   sfile = directory;
  734. }
  735.  
  736. function toggleShowHidden(event) {
  737.   treeView.showHiddenFiles = !treeView.showHiddenFiles;
  738. }
  739.  
  740. // from the current directory and whatever was entered
  741. // in the entry field, try to make a new path. This
  742. // uses "/" as the directory separator, "~" as a shortcut
  743. // for the home directory (but only when seen at the start
  744. // of a path), and ".." to denote the parent directory.
  745. // returns an array of the files listed,
  746. // or false if an error occurred.
  747. function processPath(path)
  748. {
  749.   var fileArray = new Array();
  750.   var strLength = path.length;
  751.  
  752.   if (path[0] == '"' && filePickerMode == nsIFilePicker.modeOpenMultiple &&
  753.       strLength > 1) {
  754.     // we have a quoted list of filenames, separated by spaces.
  755.     // iterate the list and process each file.
  756.  
  757.     var curFileStart = 1;
  758.  
  759.     while (1) {
  760.       var nextQuote;
  761.  
  762.       // Look for an unescaped quote
  763.       var quoteSearchStart = curFileStart + 1;
  764.       do {
  765.         nextQuote = path.indexOf('"', quoteSearchStart);
  766.         quoteSearchStart = nextQuote + 1;
  767.       } while (nextQuote != -1 && path[nextQuote - 1] == '\\');
  768.       
  769.       if (nextQuote == -1) {
  770.         // we have a filename with no trailing quote.
  771.         // just assume that the filename ends at the end of the string.
  772.  
  773.         if (!processPathEntry(path.substring(curFileStart), fileArray))
  774.           return false;
  775.         break;
  776.       }
  777.  
  778.       if (!processPathEntry(path.substring(curFileStart, nextQuote), fileArray))
  779.         return false;
  780.  
  781.       curFileStart = path.indexOf('"', nextQuote + 1);
  782.       if (curFileStart == -1) {
  783.         // no more quotes, but if we're not at the end of the string,
  784.         // go ahead and process the remaining text.
  785.  
  786.         if (nextQuote < strLength - 1)
  787.           if (!processPathEntry(path.substring(nextQuote + 1), fileArray))
  788.             return false;
  789.         break;
  790.       }
  791.       ++curFileStart;
  792.     }
  793.   } else {
  794.     // If we didn't start with a quote, assume we just have a single file.
  795.     if (!processPathEntry(path, fileArray))
  796.       return false;
  797.   }
  798.  
  799.   return fileArray;
  800. }
  801.  
  802. function processPathEntry(path, fileArray)
  803. {
  804.   var filePath;
  805.   var file;
  806.  
  807.   try {
  808.     file = sfile.clone().QueryInterface(nsILocalFile);
  809.   } catch(e) {
  810.     dump("Couldn't clone\n"+e);
  811.     return false;
  812.   }
  813.  
  814.   var tilde_file = file.clone();
  815.   tilde_file.append("~");
  816.   if (path[0] == '~' &&                        // Expand ~ to $HOME, except:
  817.       !(path == "~" && tilde_file.exists()) && // If ~ was entered and such a file exists, don't expand
  818.       (path.length == 1 || path[1] == "/"))    // We don't want to expand ~file to ${HOME}file
  819.     filePath = homeDir.path + path.substring(1);
  820.   else
  821.     filePath = path;
  822.  
  823.   // Unescape quotes
  824.   filePath = filePath.replace(/\\\"/g, "\"");
  825.   
  826.   if (filePath[0] == '/')   /* an absolute path was entered */
  827.     file.initWithPath(filePath);
  828.   else if ((filePath.indexOf("/../") > 0) ||
  829.            (filePath.substr(-3) == "/..") ||
  830.            (filePath.substr(0,3) == "../") ||
  831.            (filePath == "..")) {
  832.     /* appendRelativePath doesn't allow .. */
  833.     try{
  834.       file.initWithPath(file.path + "/" + filePath);
  835.     } catch (e) {
  836.       dump("Couldn't init path\n"+e);
  837.       return false;
  838.     }
  839.   }
  840.   else {
  841.     try {
  842.       file.appendRelativePath(filePath);
  843.     } catch (e) {
  844.       dump("Couldn't append path\n"+e);
  845.       return false;
  846.     }
  847.   }
  848.  
  849.   fileArray[fileArray.length] = file;
  850.   return true;
  851. }
  852.